#include "stdafx.h"
#include "Simulation.hpp"
#include "RandGenFast.hpp"
#include "PropertyManager.hpp"
#include "Fish.hpp"
#include "Predator.hpp"

//////////////////////////////////////////////////////////////////////////

Simulation::Simulation()
	: _numPredators(0)
{
	
}

//////////////////////////////////////////////////////////////////////////

void Simulation::update(float dt)
{
	if (PM.paused->getBoolValue()) return;

	for (int i = 0; i < (int)_agents.size(); ++i)
	{
		_agents[i]->update(dt);

		vec3 pos = _agents[i]->getPosition();
		if (pos.x < -Globals::SCENE_SIZE_X/2) pos.x = Globals::SCENE_SIZE_X + pos.x;
		else if (pos.x > Globals::SCENE_SIZE_X/2) pos.x = pos.x - Globals::SCENE_SIZE_X;
		if (pos.y < -Globals::SCENE_SIZE_Y/2) pos.y = Globals::SCENE_SIZE_Y + pos.y;
		else if (pos.y > Globals::SCENE_SIZE_Y/2) pos.y = pos.y - Globals::SCENE_SIZE_Y;
		if (pos.z < -Globals::SCENE_SIZE_Z/2) pos.z = Globals::SCENE_SIZE_Z + pos.z;
		else if (pos.z > Globals::SCENE_SIZE_Z/2) pos.z = pos.z - Globals::SCENE_SIZE_Z;
		_agents[i]->setPosition(pos);
	}

	static int numFishLast = 0;
	if (PM.numFish->getIntegerValue() != numFishLast)
	{
		setNumFish(PM.numFish->getIntegerValue());
		numFishLast = PM.numFish->getIntegerValue();
	}

	static int numPredLast = 0;
	if (PM.numPredator->getIntegerValue() != numPredLast)
	{
		setNumPredators(PM.numPredator->getIntegerValue());
		numPredLast = PM.numPredator->getIntegerValue();
	}
}

//////////////////////////////////////////////////////////////////////////

void Simulation::draw() const
{
	for (int i = 0; i < (int)_agents.size(); ++i)
		_agents[i]->draw();
}

//////////////////////////////////////////////////////////////////////////

void Simulation::addFishes(int numAgents)
{
	for (int i = 0; i < numAgents; ++i)
	{
		_agents.push_back( boost::shared_ptr<Agent>(
			new Fish(vec3(
			rnd.RandFloat(-Globals::SCENE_SIZE_X/2, Globals::SCENE_SIZE_X/2), 
			rnd.RandFloat(-Globals::SCENE_SIZE_Y/2, Globals::SCENE_SIZE_Y/2), 
			rnd.RandFloat(-Globals::SCENE_SIZE_Z/2, Globals::SCENE_SIZE_Z/2)) ) ) );
	}
}
void Simulation::removeFishes(int numAgents)
{
	//_agents.resize( std::min((unsigned int)0, _agents.size() - numAgents) );
	_agents.erase(_agents.end() - std::min(numAgents, (int)(_agents.size()-_numPredators)), _agents.end());
}

//////////////////////////////////////////////////////////////////////////

void Simulation::addPredators(int numAgents)
{
	for (int i = 0; i < numAgents; ++i)
	{
		_agents.insert( _agents.begin(), boost::shared_ptr<Agent>(
			new Predator(vec3(
				rnd.RandFloat(-Globals::SCENE_SIZE_X/2, Globals::SCENE_SIZE_X/2), 
				rnd.RandFloat(-Globals::SCENE_SIZE_Y/2, Globals::SCENE_SIZE_Y/2),
				rnd.RandFloat(-Globals::SCENE_SIZE_Z/2, Globals::SCENE_SIZE_Z/2)) ) ) );
	}
	_numPredators += numAgents;
}
void Simulation::removePredators(int numAgents)
{
	if (_numPredators == 0 || numAgents <= 0) return;
	//_agents.resize( std::min((unsigned int)0, _agents.size() - numAgents) );
	int howMany = std::min(numAgents, _numPredators);
	_numPredators -= howMany;
	_agents.erase(_agents.begin(), _agents.begin() + howMany);
}

//////////////////////////////////////////////////////////////////////////

int Simulation::tagNeighborAgents(const Agent& agent, float radius, AgentType agentType)
{
	float range;
	vec3 agentDist;
	int tagged = 0;

	for (int i = 0; i < (int)_agents.size(); ++i)
	{
		_agents[i]->untag();

		if (_agents[i].get() == &agent || 
			(agentType != AGENT_ANY && _agents[i]->getType() != agentType)) 
			continue;

		range = radius + _agents[i]->getRadius();
		agentDist = agent.getPosition() - _agents[i]->getPosition();
		if (agentDist.squaredLength() < range*range)
		{
			_agents[i]->tag();
			++tagged;
		}
	}

	return tagged;
}

//////////////////////////////////////////////////////////////////////////

void Simulation::setNumFish(int numFish)
{
	if (numFish == _agents.size()-_numPredators) return;

	if (numFish > (int)_agents.size()-_numPredators)
		addFishes(numFish - (_agents.size()-_numPredators));
	else
		removeFishes((_agents.size()-_numPredators) - numFish);
}

//////////////////////////////////////////////////////////////////////////

void Simulation::setNumPredators(int numPred)
{
	if (numPred > _numPredators)
		addPredators(numPred - _numPredators);
	else
		removePredators(_numPredators - numPred);
}

//////////////////////////////////////////////////////////////////////////